package com.yh.mybatis.session;

import com.yh.mybatis.binding.MapperRegistry;
import com.yh.mybatis.datasource.druid.DruidDataSourceFactory;
import com.yh.mybatis.datasource.pooled.PooledDataSourceFactory;
import com.yh.mybatis.datasource.unpooled.UnpooledDataSourceFactory;
import com.yh.mybatis.executor.Executor;
import com.yh.mybatis.executor.SimpleExecutor;
import com.yh.mybatis.executor.parameter.ParameterHandler;
import com.yh.mybatis.executor.resultset.DefaultResultSetHandler;
import com.yh.mybatis.executor.resultset.ResultSetHandler;
import com.yh.mybatis.executor.statement.PreparedStatementHandler;
import com.yh.mybatis.executor.statement.StatementHandler;
import com.yh.mybatis.mapping.BoundSql;
import com.yh.mybatis.mapping.Environment;
import com.yh.mybatis.mapping.MappedStatement;
import com.yh.mybatis.reflection.MetaObject;
import com.yh.mybatis.reflection.factory.DefaultObjectFactory;
import com.yh.mybatis.reflection.factory.ObjectFactory;
import com.yh.mybatis.reflection.wrapper.DefaultObjectWrapperFactory;
import com.yh.mybatis.reflection.wrapper.ObjectWrapperFactory;
import com.yh.mybatis.scripting.LanguageDriver;
import com.yh.mybatis.scripting.LanguageDriverRegistry;
import com.yh.mybatis.scripting.defaults.DefaultParameterHandler;
import com.yh.mybatis.scripting.xmltags.XMLLanguageDriver;
import com.yh.mybatis.transaction.Transaction;
import com.yh.mybatis.transaction.jdbc.JdbcTransactionFactory;
import com.yh.mybatis.type.TypeAliasRegistry;
import com.yh.mybatis.type.TypeHandlerRegistry;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

/**
 * 配置项
 *
 * @author yh
 */
public class Configuration {
    /**
     * 环境
     */
    protected Environment environment;
    /**
     * 映射器注册表
     */
    protected MapperRegistry mapperRegistry = new MapperRegistry(this);
    /**
     * 映射语句  存在Map里面
     */
    protected final Map<String, MappedStatement> mappedStatements = new HashMap<>();
    /**
     * 类型别名注册表
     */
    protected final TypeAliasRegistry typeAliasRegistry = new TypeAliasRegistry();
    protected final LanguageDriverRegistry languageRegistry = new LanguageDriverRegistry();

    /**
     * 类型处理程序注册表
     */
    protected final TypeHandlerRegistry typeHandlerRegistry = new TypeHandlerRegistry();

    /**
     * 对象工厂和对象包装器工厂
     */
    protected ObjectFactory objectFactory = new DefaultObjectFactory();
    protected ObjectWrapperFactory objectWrapperFactory = new DefaultObjectWrapperFactory();

    protected final Set<String> loadedResources = new HashSet<>();
    protected String databaseId;

    public Configuration() {
        typeAliasRegistry.registryAlias("JDBC", JdbcTransactionFactory.class);
        typeAliasRegistry.registryAlias("DRUID", DruidDataSourceFactory.class);
        typeAliasRegistry.registryAlias("UNPOOLED", UnpooledDataSourceFactory.class);
        typeAliasRegistry.registryAlias("POOLED", PooledDataSourceFactory.class);

        languageRegistry.setDefaultDriverClass(XMLLanguageDriver.class);
    }

    public void addMappers(String packageName) {
        mapperRegistry.addMappers(packageName);
    }

    public <T> void addMapper(Class<T> type) {
        mapperRegistry.addMapper(type);
    }

    public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
        return mapperRegistry.getMapper(type, sqlSession);
    }

    public boolean hasMapper(Class<?> type) {
        return mapperRegistry.hasMapper(type);
    }

    public void addMappedStatement(MappedStatement ms) {
        mappedStatements.put(ms.getId(), ms);
    }

    public MappedStatement getMappedStatement(String id) {
        return mappedStatements.get(id);
    }


    public TypeAliasRegistry getTypeAliasRegistry() {
        return typeAliasRegistry;
    }

    public Environment getEnvironment() {
        return environment;
    }

    public void setEnvironment(Environment environment) {
        this.environment = environment;
    }

    public String getDatabaseId() {
        return databaseId;
    }

    /**
     * 创建结果集处理器
     *
     * @param executor        执行器
     * @param mappedStatement 映射语句
     * @param boundSql        绑定sql
     * @return {@link ResultSetHandler}
     */
    public ResultSetHandler newResultSetHandler(Executor executor, MappedStatement mappedStatement, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
        return new DefaultResultSetHandler(executor, mappedStatement, resultHandler, rowBounds, boundSql);
    }

    /**
     * 生产执行器
     *
     * @param transaction 交易
     * @return {@link Executor}
     */
    public Executor newExecutor(Transaction transaction) {
        return new SimpleExecutor(this, transaction);
    }

    /**
     * 创建语句处理器
     *
     * @param executor        执行器
     * @param mappedStatement 映射语句
     * @param parameter       参数
     * @param resultHandler   结果处理程序
     * @param boundSql        绑定sql
     * @return {@link StatementHandler}
     */
    public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
        return new PreparedStatementHandler(executor, mappedStatement, parameter, rowBounds, resultHandler, boundSql);
    }

    /**
     * 创建元对象
     *
     * @param object 对象
     * @return {@link MetaObject}
     */
    public MetaObject newMetaObject(Object object) {
        return MetaObject.forObject(object, objectFactory, objectWrapperFactory);
    }

    public TypeHandlerRegistry getTypeHandlerRegistry() {
        return typeHandlerRegistry;
    }

    public boolean isResourceLoaded(String resource) {
        return loadedResources.contains(resource);
    }

    public LanguageDriverRegistry getLanguageRegistry() {
        return languageRegistry;
    }

    public void addLoadedResource(String resource) {
        loadedResources.add(resource);
    }

    public LanguageDriver getDefaultScriptLanguageInstance() {
        return languageRegistry.getDefaultDriver();
    }

    /**
     * 创建参数处理器
     *
     * @param mappedStatement 映射语句
     * @param parameterObject 参数对象
     * @param boundSql        绑定sql
     * @return {@link ParameterHandler}
     */
    public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
        return new DefaultParameterHandler(mappedStatement, parameterObject, boundSql);
    }

    public ObjectFactory getObjectFactory() {
        return objectFactory;
    }
}
